using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace Spiritmind
{
    public partial class FormMain : Form
    {
        private ThreadStart pinger;
        private Thread[] threads;
        private List<HostPinged> results;
        private Words wordlist;
        private string domain;
        public static string file;
        
        public FormMain()
        {
            InitializeComponent();
            cbThreads.Text = "1";
            pinger = new ThreadStart(PingHost);
        }

        #region Menu actions
        private void mnExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void cbThreads_Leave(object sender, EventArgs e)
        {
            char c = cbThreads.Text[0];
            if (Char.IsNumber(c))
            {
                int n = (int)c - 48;
                if (n > 5 || n < 1)
                {
                    cbThreads.Text = "1";
                }
            }
            else
            {
                cbThreads.Text = "1";
            }
        }

        private void mnAbout_Click(object sender, EventArgs e)
        {
            About about = new About();
            about.ShowDialog(this);
        }

        private void mnOptions_Click(object sender, EventArgs e)
        {
            Options options = new Options();
            options.ShowDialog(this);
        }
        #endregion

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(tbHost.Text))
            {
                MessageBox.Show("Please, enter a domain", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            btnStart.Enabled = false;

            int nt = (int)cbThreads.Text[0] - 48;
            threads = new Thread[nt];

            // Clear data
            results = new List<HostPinged>(nt);
            lbHosts.Items.Clear();
            try
            {
                wordlist = new Words(file);
                wordlist.Reset();

                lblProgress.Text = "0";
                lblTotal.Text = "/ " + wordlist.Count();

                // Get the domain
                domain = tbHost.Text;

                // Check for http in the start of the string
                if (domain.Contains("http://"))
                {
                    int n = domain.IndexOf('/');
                    domain = domain.Substring(n + 2); // We need the position after the second /
                }
                // We dont need the www protocol for nothing!
                if (domain.Contains("www"))
                {
                    int n = domain.IndexOf('.');
                    domain = domain.Substring(n + 1);
                }

                for (int i = 0; i < threads.Length; i++)
                {
                    threads[i] = new Thread(pinger);
                }

                foreach (Thread t in threads)
                {
                    t.Start();
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                EnableStart();
            }
        }

        private void PingHost()
        {
            string sub = wordlist.Current;
            Ping ping = new Ping();
            string host = "";
            while (!String.IsNullOrEmpty(sub))
            {
                host = sub + "." + domain;

                try
                {
                    PingReply pingreply = ping.Send(host);
                    results.Add(new HostPinged(false, host + " - " + pingreply.Address.ToString()));
                    WriteResult();
                }
                catch (ArgumentException) { /* The subdomain has some invalid characters */ }
                catch (PingException) { /* The domain doesn't exists or its firewalled */ }
                finally 
                { 
                    sub = wordlist.Current;
                    UpdateStatusBar();
                }
            }
            EnablebtnStart();
        }

        #region Invokes
        private void EnablebtnStart()
        {
            if (btnStart.InvokeRequired)
            {
                btnStart.Invoke(new MethodInvoker(EnableStart));
            }
        }

        private void WriteResult()
        {
            if (lbHosts.InvokeRequired)
            {
                lbHosts.Invoke(new MethodInvoker(FillListbox));
            }
        }

        private void UpdateStatusBar()
        {
            if (statusStrip1.InvokeRequired)
            {
                statusStrip1.Invoke(new MethodInvoker(AddCounter));
            }
        }

        public void AddCounter()
        {
            int n = Convert.ToInt32(lblProgress.Text);
            n++;
            lblProgress.Text = n.ToString();
        }
        
        public void EnableStart()
        {
            btnStart.Enabled = true;
            GC.Collect();
        }

        private void FillListbox()
        {
            foreach (HostPinged hostpinged in results)
            {
                if (!hostpinged.Printed)
                {
                    lbHosts.Items.Add(hostpinged.Host);
                    hostpinged.Printed = true;
                }
            }
        }
        #endregion

        #region Internal class HostPinged
        internal class HostPinged
        {
            public bool Printed;
            public string Host;

            public HostPinged(bool printed, string host) 
            {
                Printed = printed;
                Host = host;
            }
        }
        #endregion

        #region Dictionary class
        internal class Words
        {
            int _index = 0;
            string[] words;

            public Words(string file)
            {
                try
                {
                    words = File.ReadAllLines(file);
                }
                catch (ArgumentNullException)
                {
                    throw new Exception("Please, select a dictionary file.");
                }
                catch (FileNotFoundException e)
                {
                    throw new Exception("The file " + e.FileName + " can not be found. Please, check it.");
                }
                catch (UnauthorizedAccessException)
                {
                    throw new Exception("The file can not be open");
                }
            }

            public string Current
            {
                get 
                {
                    if (_index < words.Length)
                        return words[_index++];
                    else   
                        return String.Empty;
                }
            }

            public void Reset()
            {
                _index = 0;
            }

            public int Count()
            {
                return words.Length;
            }
        }
        #endregion

        private void cmnStrip_Opened(object sender, EventArgs e)
        {
            if (lbHosts.Text == String.Empty)
                cmnStrip.Enabled = false;
            else
                cmnStrip.Enabled = true;
        }

        private void copyHostToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string[] s = {" - "};
            Clipboard.SetText((lbHosts.Text.Split(s, StringSplitOptions.RemoveEmptyEntries)[0]));
            
        }

        private void copyIpToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string[] s = { " - " };
            Clipboard.SetText((lbHosts.Text.Split(s, StringSplitOptions.RemoveEmptyEntries)[1]));
        }

        private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                foreach (Thread t in threads)
                {
                    t.Abort();
                }
            }
            catch (NullReferenceException) { }
        }

        

    }
}